package com.drakulo.games.ais.ui;

import java.util.ArrayList;
import java.util.List;

import org.newdawn.slick.tiled.TiledMap;

import com.drakulo.games.ais.core.Colony;
import com.drakulo.games.ais.core.GameData;
import com.drakulo.games.ais.core.building.Building;
import com.drakulo.games.ais.core.building.BuildingType;

public final class TileHelper {
	public static final int ACCESS_LAYER = 1;
	public static final int DECO_LAYER = 0;

	public static final int DEFAULT_TILE = 2;
	public static final int BUILDABLE_TILE = 3;

	/**
	 * Tests if a tile is buildable according to the building type. Buildings
	 * can only be built around a road. Roads can be built around other roads
	 * and buildings
	 * 
	 * @param x
	 *            X coordinate of the tile
	 * @param y
	 *            Y coordinate of the tile
	 * @param type
	 *            the building type
	 * @return true if the building type can be built on the given tile
	 */
	public static boolean tileIsBuildable(int x, int y, BuildingType type) {
		TiledMap map = GameData.getSelectedColony().getMap();
		final int width = map.getWidth();
		final int height = map.getHeight();
		if (x < 0 || y < 0 || x > width || y > height) {
			// Coords are out of the map
			return false;
		}

		if (!tileIsPrepared(x, y)) {
			// The tile is not prepared
			return false;
		}

		if (BuildingType.ROAD.equals(type)) {
			// Roads must be near buildings or other roads
			return isNearRoad(x, y) || isNearBuilding(x, y);
		} else {
			// buildings must be near roads
			return isNearRoad(x, y);
		}
	}

	/**
	 * Tests if there is a road on the 4 squares near the given tile
	 * 
	 * @param x
	 *            X coordinate of the tile
	 * @param y
	 *            Y coordinate of the tile
	 * @return true if a road is found
	 */
	public static boolean isNearRoad(int x, int y) {
		return typeIsNear(x, y, BuildingType.ROAD);
	}

	/**
	 * Tests if there is a building on the 4 squares near the given tile
	 * 
	 * @param x
	 *            X coordinate of the tile
	 * @param y
	 *            Y coordinate of the tile
	 * @return true if a building is found
	 */
	public static boolean isNearBuilding(int x, int y) {
		List<Building> buildings = searchNearBuildings(x, y);
		return buildings.size() > 0;
	}

	public static boolean typeIsNear(int x, int y, BuildingType type) {
		List<Building> buildings = searchNearBuildings(x, y);
		for (Building b : buildings) {
			if (type.equals(b.getType())) {
				return true;
			}
		}
		return false;
	}

	private static List<Building> searchNearBuildings(int x, int y) {
		List<Building> buildings = new ArrayList<Building>();
		safeAdd(buildings, GameData.getSelectedColony().getBuildingAt(x, y - 1));
		safeAdd(buildings, GameData.getSelectedColony().getBuildingAt(x + 1, y));
		safeAdd(buildings, GameData.getSelectedColony().getBuildingAt(x, y + 1));
		safeAdd(buildings, GameData.getSelectedColony().getBuildingAt(x - 1, y));
		return buildings;
	}

	private static void safeAdd(List<Building> list, Building b) {
		if (b == null) {
			return;
		}
		list.add(b);
	}

	/**
	 * Tests if a tile is impossible to access and prepare
	 * 
	 * @param x
	 *            X coordinate of the tile
	 * @param y
	 *            Y coordinate of the tile
	 * @return true if the tile is impossible to access
	 */
	public static boolean tileIsImpossible(int x, int y) {
		if (x < 0 || y < 0) {
			return false;
		}
		int tileID = GameData.getCurrentMap().getTileId(x, y, ACCESS_LAYER);
		return tileID != 0;
	}

	/**
	 * Tests if the tile is prepared by a terrabot
	 * 
	 * @param x
	 *            X coordinate of the tile
	 * @param y
	 *            Y coordinate of the tile
	 * @return true if the tile is prepared
	 */
	private static boolean tileIsPrepared(int x, int y) {
		int tileID = GameData.getCurrentMap().getTileId(x, y, 0);
		return tileID == BUILDABLE_TILE;
	}

	/**
	 * Tests if a tile is preparable by a terrabot
	 * 
	 * @param x
	 *            X coordinate of the tile
	 * @param y
	 *            Y coordinate of the tile
	 * @return true if a terrabot can prepare the tile
	 */
	public static boolean tileIsPreparable(int x, int y) {
		boolean isNotPrepared = !tileIsPrepared(x, y);
		boolean isNotImpossible = !tileIsImpossible(x, y);
		return isNotPrepared && isNotImpossible;
	}

	/**
	 * Update the given tile and set it to the given ID
	 * 
	 * @param x
	 *            X coordinate of the tile
	 * @param y
	 *            Y coordinate of the tile
	 * @param tileID
	 *            the tile ID
	 */
	public static void setTile(int x, int y, int tileID, Colony c) {
		c.getMap().setTileId(x, y, 0, tileID);
	}
	
	/**
	 * Tests if a tile is valid for command center construction
	 * 
	 * @param x
	 *            X coordinate of the tile
	 * @param y
	 *            Y coordinate of the tile
	 * @return true if the tile is OK
	 */
	public static boolean isValidCommandCenterTile(int x, int y){
		TiledMap map = GameData.getSelectedColony().getMap();
		final int width = map.getWidth();
		final int height = map.getHeight();
		if(x < 1 || y < 1 || x > width || y > height){
			return false;
		}
		return true;
	}
}
